package cn.nkk.hikvision.factory;


import cn.hutool.core.util.StrUtil;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * flv转换器
 *
 * @author dlj
 * @date 2023/03/29
 */

/**
 * flv转换器
 *
 * @author dlj
 * @date 2023/03/29
 */
public class FlvSocketConverter extends Thread implements Converter {

    private static final Logger log = LoggerFactory.getLogger(FlvSocketConverter.class);
    private byte[] headers;
    private String rtspUrl;
    private WebSocketSession context;




    public FlvSocketConverter(String rtspUrl, WebSocketSession context) {
        this.rtspUrl = rtspUrl;
        this.context = context;
    }


    @Override
    public void run() {
        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(rtspUrl);
        FFmpegFrameRecorder recorder = null;
        ByteArrayOutputStream stream = null;
        try {
            avutil.av_log_set_level(avutil.AV_LOG_ERROR);
            log.info("进入grabber抓流");
            ;
            if (StrUtil.isNotEmpty(rtspUrl) && rtspUrl.startsWith("rtsp")) {
                grabber.setOption("rtsp_transport", "tcp");
                // 减少初始分析时间（单位：微秒）
                grabber.setOption("analyzeduration", "100000");  // 100毫秒
                // 限制输入数据量检查
                grabber.setOption("probesize", "1024");  // 限制探测数据为1024字节
                // 禁用格式检查
                grabber.setOption("fflags", "nobuffer+discardcorrupt");
                grabber.setOption("buffer_size", "10240");
                log.info("rtsp链接");
            }
            grabber.startUnsafe();
            int videoCodec = grabber.getVideoCodec();
            log.info("启动grabber,编码{}", videoCodec);
            grabber.setImageWidth(640);
            grabber.setImageHeight(480);

            stream = new ByteArrayOutputStream(1024 * 1024);
            recorder = new FFmpegFrameRecorder(stream, grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels());
            recorder.setInterleaved(true);
            recorder.setVideoOption("preset", "ultrafast");
            recorder.setVideoOption("tune", "zerolatency");
            recorder.setVideoOption("crf", "18");
            recorder.setGopSize(25);
            recorder.setFrameRate(25);
            recorder.setSampleRate(grabber.getSampleRate());
            if (grabber.getAudioChannels() > 0) {
                recorder.setAudioChannels(grabber.getAudioChannels());
                recorder.setAudioBitrate(grabber.getAudioBitrate());
                recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
            }
            recorder.setFormat("flv");
            recorder.setVideoBitrate(grabber.getVideoBitrate());
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            recorder.start();
            if (headers == null) {
                headers = stream.toByteArray();
                stream.reset();
                writeResponse(headers);
            }

            while (true) {
                Frame f = grabber.grab();
                if (f == null) {
                    break;
                }
                // 转码
                recorder.record(f);
                if (stream.size() > 0) {
                    byte[] byteArray = stream.toByteArray();
                    stream.reset();
                    try {
                        context.sendMessage(new BinaryMessage(byteArray));
                    } catch (Exception e) {
                        context.close();
                        break;
                    }
                }
            }
        } catch (Exception e) {
            log.error("异步出错,{}", e.getMessage());
        } finally {
            try {
                grabber.close();
                if (recorder != null) recorder.close();
                if (stream != null) stream.close();
                System.gc();
                context.close();
            } catch (Exception e) {
                log.error("资源回收,{}", e.getMessage());
            }
        }

    }


    /**
     * 依次写出队列中的上下文
     *
     * @param bytes bytes
     */
    public void writeResponse(byte[] bytes) throws IOException {
        try {
            context.sendMessage(new BinaryMessage(bytes));
        } catch (Exception e) {
            context.close();
        }
    }


    @Override
    public String getKey() {
        return null;
    }
}
